import cv2
import numpy as np
import logging
import random
from config import config
from utils import setup_logger, generate_temp_path

logger = setup_logger()

class VideoFrameProcessor:
    def __init__(self):
        self.last_angle = 0.0  # 上一帧的旋转角度，用于平滑过渡（初始为0度）
        self.last_scale = 1.0  # 上一帧的缩放比例，用于平滑过渡（初始为1.0倍）
        self.smooth_factor = 0.3  # 平滑因子，控制帧间变化幅度（0.0=无平滑，1.0=完全平滑）

    def process_frame(self, frame: np.ndarray) -> np.ndarray:
        """处理单帧图像，应用多种变换以避免去重检测"""
        # 随机帧丢弃 - 模拟网络丢帧
        if random.random() < config.frame_drop_rate:  # 根据配置的概率丢弃帧
            return None  # 返回None表示丢弃此帧

        h, w = frame.shape[:2]  # 获取帧的高度和宽度

        # 1. 几何变换（旋转、平移、缩放）- 使用平滑过渡
        target_angle = random.uniform(-config.rotation_range, config.rotation_range)  # 生成目标旋转角度（从配置的范围中随机选择）
        target_scale = random.uniform(*config.scale_range)  # 生成目标缩放比例（从配置的范围中随机选择）

        # 平滑过渡，避免剧烈抖动
        # 当前角度 = 上一帧角度 * (1-平滑因子) + 目标角度 * 平滑因子
        angle = self.last_angle * (1 - self.smooth_factor) + target_angle * self.smooth_factor
        # 当前缩放 = 上一帧缩放 * (1-平滑因子) + 目标缩放 * 平滑因子
        scale = self.last_scale * (1 - self.smooth_factor) + target_scale * self.smooth_factor

        self.last_angle = angle  # 保存当前角度，供下一帧使用
        self.last_scale = scale  # 保存当前缩放，供下一帧使用

        # 创建旋转矩阵：中心点旋转，应用角度和缩放
        M = cv2.getRotationMatrix2D((w/2, h/2), angle, scale)
        # 应用仿射变换到整个帧
        frame = cv2.warpAffine(frame, M, (w, h))

        # 2. 颜色空间扰动（HSV空间）- 改变颜色和亮度
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)  # 将BGR转换为HSV颜色空间
        hsv = hsv.astype(np.float32)  # 转换为浮点数以便进行数学运算

        # 亮度调整 - 影响整体明暗
        hsv[:, :, 2] = np.clip(hsv[:, :, 2] * random.uniform(*config.brightness_range), 0, 255)  # 调整V通道（亮度），限制在0-255范围内
        # 饱和度调整 - 影响颜色鲜艳程度
        hsv[:, :, 1] = np.clip(hsv[:, :, 1] * random.uniform(*config.saturation_range), 0, 255)  # 调整S通道（饱和度），限制在0-255范围内

        # 将HSV转换回BGR颜色空间
        frame = cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2BGR)

        # 3. 高频噪声注入 - 添加细微的像素级扰动
        noise = np.random.normal(0, 0.28, frame.shape).astype(np.uint8)  # 生成均值为0，标准差为0.28的高斯噪声
        frame = cv2.add(frame, noise)  # 将噪声叠加到原始帧上

        return frame  # 返回处理后的帧

class VideoProcessor:
    def __init__(self):
        self.frame_processor = VideoFrameProcessor()  # 初始化帧处理器

    def process_video(self, input_path: str, output_path: str = None, progress_callback=None) -> str:
        """处理视频文件，应用帧级变换"""
        if output_path is None:  # 如果没有指定输出路径
            output_path = generate_temp_path(prefix="video_processed", suffix=".mp4")  # 生成临时输出路径

        try:
            # 打开视频文件
            cap = cv2.VideoCapture(input_path)  # 创建视频读取对象
            fps = cap.get(cv2.CAP_PROP_FPS)  # 获取视频帧率
            width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # 获取视频宽度
            height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # 获取视频高度
            frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # 获取总帧数

            logger.info(f"加载视频: {input_path}, 分辨率: {width}x{height}, FPS: {fps}, 帧数: {frame_count}")  # 记录视频信息

            # 创建输出视频写入器
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # 设置视频编码格式为MP4
            out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))  # 创建视频写入对象

            # 逐帧处理
            processed_frames = 0  # 计数已处理的帧数
            while True:  # 循环读取每一帧
                ret, frame = cap.read()  # 读取一帧
                if not ret:  # 如果读取失败（到达文件末尾）
                    break  # 退出循环

                processed_frame = self.frame_processor.process_frame(frame)  # 处理当前帧
                if processed_frame is not None:  # 如果帧没有被丢弃
                    out.write(processed_frame)  # 将处理后的帧写入输出文件
                    processed_frames += 1  # 增加处理帧计数

                # 调用进度回调
                if progress_callback:
                    progress_callback(processed_frames, frame_count)

            # 释放资源
            cap.release()  # 释放视频读取对象
            out.release()  # 释放视频写入对象

            logger.info(f"视频处理完成，总帧数: {frame_count}, 处理帧数: {processed_frames}, 保存至: {output_path}")  # 记录处理结果
            return output_path  # 返回输出文件路径
        except Exception as e:  # 如果发生任何异常
            logger.error(f"视频处理失败: {str(e)}")  # 记录错误信息
            raise  # 重新抛出异常